<?php
/**
 * OA Session Manager
 *
 * $Id: OA_Session.php 153 2011-09-27 13:32:00Z alpha1130@gmail.com $
 */

class OA_Session extends CI_Session
{
	
	private $access_log_table_name = 'access_log';
	
	private $uid = 0;
	private $user = array();
	private $role = array();
	private $is_master = FALSE;
	
	public function get_uid()
	{
		return $this->uid;
	}
	
	public function get_user()
	{
		return $this->user;
	}
	
	public function get_role()
	{
		return $this->role;
	}
	
	public function is_master()
	{
		return $this->is_master;
	}
	
	public function get_verify_image()
	{
		
		$pool = '23456789abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ';
		$word = '';
		for ($i = 0; $i < 6; $i++)
		{
			$word .= substr($pool, mt_rand(0, strlen($pool) -1), 1);
		}
		
		$this->CI->load->helper('captcha');
		$captcha = create_captcha(array(
			'word' => $word,
			'expiration' => 120,
    		'img_path' => './captcha/',
    		'img_url' => '/captcha/'
		));
		$this->set_flashdata('verify_word', $captcha['word']);
		
		return $captcha['image'];
	}
	
	public function check_verify_image($verify_word)
	{
		return strtolower($this->flashdata('verify_word')) === strtolower($verify_word);
	}
	
	const LOGIN_FAILED_COUNT_PERIOD = 5;
	const LOGIN_FAILED_FREEZED_TIME = 600;
	
	const ERROR_LOGIN_FAILED = -1;
	const ERROR_LOGIN_FAILED_AND_LOCKED = -2;
	
	/**
	 * check user login
	 *
	 * @param string $username
	 * @param string $password
	 */
	public function check_login($username, $password)
	{
		$login_count = intval($this->userdata('login_count'));
		$last_activity = intval($this->userdata('last_activity'));
		
		// login too many times
		if($login_count > 0 && 
			$login_count % self::LOGIN_FAILED_COUNT_PERIOD == 0 &&
			$last_activity + self::LOGIN_FAILED_FREEZED_TIME > $_SERVER['REQUEST_TIME'])
		{
			return self::ERROR_LOGIN_FAILED_AND_LOCKED;
		}
		
		$user = $this->CI->db
			->get_where('user', array('username' => $username))
			->row_array();
		
		// user not exists, or password incorrect
		if($user == NULL || $user['password'] !== md5($password))
		{
			$this->set_userdata('login_count', $login_count+1);
			return self::ERROR_LOGIN_FAILED;
		}
		
		$this->set_userdata('uid', $user['id']);
		$this->set_userdata('login_time', $_SERVER['REQUEST_TIME']);
	}
	
	const ERROR_ACCESS_OK = 0;
	const ERROR_NOT_LOGIN = -1;
	const ERROR_USER_NOT_EXISTS = -2;
	const ERROR_USER_HAS_BEEN_DELETED = -3;
	const ERROR_USER_HAS_BEEN_BLOCKED = -4;
	const ERROR_PERMISSION_DENIED = -5;
	
	/**
	 * check user access control
	 *
	 * @param string $class
	 * @param string $method
	 */
	function check_acl($class, $method)
	{
		// check user
		$uid = $this->userdata('uid');
		if($uid == NULL)
		{
			return self::ERROR_NOT_LOGIN;
		}
		
		$user = $this->CI->db
			->select('*')->where('id', $uid)->get('user')
			->row_array();
		
		if($user == NULL)
		{
			return self::ERROR_USER_NOT_EXISTS;
		}
		if($user['status'] == -1)
		{
			return self::ERROR_USER_HAS_BEEN_DELETED;
		}
		if($user['status'] == -2)
		{
			return self::ERROR_USER_HAS_BEEN_BLOCKED;
		}
		
		$this->uid = $uid;
		$this->user = $user;
		
		// master
		$master = $this->CI->config->item('master');
		if($master != NULL && in_array($this->uid, explode(',', $master)))
		{
			$this->is_master = TRUE;
			return self::ERROR_ACCESS_OK;
		}
		
		// user or admin ?
		$roles = $this->CI->db
			->select('r_id')
			->where('u_id', $this->uid)
			->get('user_role')
			->result_array();
		
		// my role is empty
		if($roles == NULL)
		{
			return self::ERROR_PERMISSION_DENIED;
		}
		
		foreach($roles as $_role)
		{
			$this->role[] = $_role['r_id'];
		}
		
		// do not has permission with current class func
		$is_has_perm = $this->CI->db
			->select('r_id')
			->from('class_func_role')
			->join('class_func_description',
				'class_func_role.cf_id=class_func_description.id')
			->where_in('r_id', $this->role)
			->get_where('', array(
				'class' => $class, 
				'func' => $method))
			->row_array();
		if($is_has_perm == NULL)
		{
			return self::ERROR_PERMISSION_DENIED;
		}
		
		return self::ERROR_ACCESS_OK;
	}
	
	function _add_access_log($uid, $ip_address, $login_time, $logout_time, $is_has_operate)
	{
		$this->CI->db->insert($this->access_log_table_name, array(
			'user_id' => $uid,
			'ip_address' => $ip_address,
			'login_time' => $login_time,
			'logout_time' => $logout_time,
			'is_has_operate' => $is_has_operate
		));
	}
	
	function sess_destroy()
	{
		if($this->uid)
		{
			$this->_add_access_log(
				$this->uid, 
				$this->userdata('ip_address'),
				$this->userdata('login_time'),
				$this->now,
				0
			);
		}
		parent::sess_destroy();
	}
	
	function _sess_gc()
	{
		if ($this->sess_use_database != TRUE)
		{
			return;
		}
		
		srand(time());
		if ((rand() % 100) < $this->gc_probability)
		{
			$expire = $this->now - $this->sess_expiration;
			$query = $this->CI->db
				->where("last_activity < {$expire}")
				->get($this->sess_table_name);
			
			foreach($query->result_array() as $row)
			{
				if($row['last_activity'] < $expire)
				{
					$user_data = unserialize($row['user_data']);
					if(!is_array($user_data) 
					|| !array_key_exists('uid', $user_data)
					|| !array_key_exists('login_time', $user_data))
					{
						continue;
					}
					$row += $user_data;
					$this->_add_access_log(
						$row['uid'], 
						$row['ip_address'],
						$row['login_time'],
						$this->now,
						0
					);
				}
			}
			
			$this->CI->db
				->where("last_activity < {$expire}")
				->delete($this->sess_table_name);
			
			log_message('debug', 'Session garbage collection performed.');
		}
	}
	
}

/* End file of OA_Session.php */
/* Location: ./application/libraries/OA_Session.php */
